package com.template.controller;

import cn.hutool.core.map.MapUtil;
import com.template.common.exception.CommonException;
import com.template.common.lang.BaseContext;
import com.template.common.lang.Const;
import com.template.common.lang.R;
import com.template.domain.param.AuthParams;
import com.template.domain.vo.AuthVo;
import com.template.service.SysMenuService;
import com.template.service.SysUserService;
import com.template.utils.CaptchaUtils;
import com.template.utils.JwtUtils;
import com.template.utils.ValidUtils;
import com.template.utils.redis.StringOps;
import com.wf.captcha.base.Captcha;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.awt.*;
import java.io.IOException;
import java.util.*;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {

    private final HttpServletRequest request;

    private final StringOps stringOps;

    private final SysUserService sysUserService;

    private final SysMenuService sysMenuService;

    private final CaptchaUtils captchaUtils;

    private final JwtUtils jwtUtils;

    /**
     * 获取图形验证码
     */
    @GetMapping("/captcha")
    public R<AuthVo.Captcha> getCaptcha() {
        // 生成图形验证码
        Captcha captcha;
        try {
            captcha = CaptchaUtils.createCaptcha();
        } catch (IOException | FontFormatException e) {
            throw new CommonException("获取图形验证码失败");
        }
        // 缓存验证码结果 并获取缓存的key
        String key = captchaUtils.cacheCaptcha(captcha);

        AuthVo.Captcha captchaVo = new AuthVo.Captcha(captcha.toBase64(), key);
        return R.success(captchaVo);
    }

    /**
     * 验证图形验证码
     */
    @PostMapping("/valid-captcha")
    public R<String> validCaptcha(@RequestBody @Valid AuthParams.ValidCaptcha params) {
        boolean flag = captchaUtils.verifyCaptcha(params.getKey(), params.getCode());
        return flag ? R.success() : R.fail("图形验证码错误");
    }

    /**
     * 获取手机验证码
     */
    @GetMapping("/phone-code")
    public R<String> phoneCode(String phone) {
        if (!ValidUtils.phone(phone))
            return R.fail("手机号不正确，请重新输入");

        String key = Const.cachePhoneCode(phone);

        if (stringOps.exists(key))
            return R.fail("操作频繁，请稍后重试");

        // 发送验证码
        Random random = new Random();
        int code = random.nextInt(4);
        log.info("发送手机验证码：phone = {}，code = {}", phone, code);

        // 记录手机号，防止频繁发送短信
        stringOps.set(key, code, 60);

        return R.success();
    }

    /**
     * 账号密码登录
     */
    @PostMapping("/login")
    public R<Map<String, String>> login(@Valid @RequestBody AuthParams.Login param) {
        String token = sysUserService.login(param);

        Map<String, String> rMap = MapUtil.builder(Const.Header.AUTHORITY, token).map();
        return R.success(rMap);
    }

    /**
     * 手机号登录（未完善）
     */
    @PostMapping("/login/phone")
    public R<Map<String, String>> loginByPhone(@Valid @RequestBody AuthParams.LoginByPhone param) {
        String token = sysUserService.loginByPhone(param);

        Map<String, String> rMap = MapUtil.builder(Const.Header.AUTHORITY, token).map();
        return R.success(rMap);
    }

    /**
     * 刷新token
     */
    @PostMapping("/refresh-token")
    public R<Map<String, String>> refreshToken() {
        Long userId = BaseContext.getCurrentId();
        String oldToken = request.getHeader(Const.Header.AUTHORITY);

        // 双token机制刷新
        String newToken = jwtUtils.refreshToken(userId, oldToken);

        Map<String, String> rMap = new HashMap<>() {{
            put(Const.Header.AUTHORITY, newToken);
        }};
        return R.success(rMap);
    }

    /**
     * 登出
     */
    @PostMapping("/logout")
    public R<String> logout() {
        Long userId = BaseContext.getCurrentId();
        jwtUtils.removeToken(userId);
        return R.success();
    }

    @GetMapping("/keepalive")
    public R<List<String>> getKeepAliveRoutes() {
        List<String> keepalive = sysMenuService.getKeepaliveMenu();
        return R.success(keepalive);
    }
}
